---
title: Environment Variables and Configuration
description: Environment variables and configuration with validation and type-checking.
head:
  - tag: title
    content: Environment Variables and Configuration | React Native / Expo Starter
---

import Code from '../../../components/code.astro';

Managing environment variables in your project is an essential task, but it can also be challenging. That's why we have included a complete setup for environment variables in this project. This setup comes with validation and type-checking using the `zod` library.

All the code related to environment variables is located in the `env.js` and `src/lib/env.js` files. The `env.js` read the `APP_ENV` variable and loads the correct `.env` file, then defines the `zod` schema for the environment variables for client and build-time, parses the `_env` object, and returns the parsed object, or throws errors in case of invalid or missing variables.

To increase security, we are splitting environment variables into two parts:

- **Client Variables**: Variables that are safe to be exposed to the client and used in your `src` folder. These variables are passed to the client side using the `extra` configuration in the `app.config.ts` file.
- **Build Time Variables**: Variables that we don't need on the client-side and are only used in the `app.config.ts`, for example `SENTRY_AUTH` to upload the source maps to Sentry.

By using this pre-configured setup for environment variables, you can focus on building your project without worrying about managing and validating your environment variables.

This setup is highly inspired by [T3 Stack](https://create.t3.gg/) 👌

## Adding a new environment variable to the project.

To add a new environment variable to the project, follow these steps:

1. Add the new environment variable to the correct `zod` schema inside the `env.js` file based on this simple rule :
   If the variable is used in the `src` folder, add it to the `client` schema, otherwise add it to the `buildTime` schema.

This will ensure that the new variable is validated correctly. and make sure we are only sending the correct vars to the client side
Here's an example:

```js title="env.js"
const client = z.object({
  // ...
  // add the new environment variable here/ accessible on the client side and build time(app.config.ts)
  NEW_ENV_VAR: z.string(),
});

const buildTime = z.object({
  // ...
  // add the new environment variable here / accessible only on build time(app.config.ts)
  NEW_SECRET_ENV: z.string(),
});
```

2. Add the new environment variable to the correct env object inside the `env.js`, `_clientEnv` for client variables and `_buildTimeEnv` for build time variables. Here's an example:

```js title="env.js"
const _clientEnv = {
  // ...
  // add the new environment variable here
  NEW_ENV_VAR: process.env.NEW_ENV_VAR,
};

const _buildTimeEnv = {
  // ...
  // add the new environment variable here
  NEW_SECRET_ENV: process.env.NEW_SECRET_ENV,
};
```

3. Add the new environment variable to your `.env` files. Make sure to include it in all relevant files (`development`, `staging`, and `production`). Here's an example:

```bash title=".env.{APP_ENV}"
# ...
# add the new environment variable here
NEW_ENV_VAR=my-new-var
```

:::note
if you are not pushing env files to your repo(recomended), please make sure to check the [App releasing process](/ci-cd/app-releasing-process/#github-action-and-env-variables) to see how to create the env file on the fly before the prebuild script in the github actions.
:::

4. Make sure to run `pnpm prebuild` to load the new values.

```bash
pnpm prebuild
```

5. The new environment variable is now ready to use in your project. You can access it in your code using the `Env` object, like this:

```ts title="client.ts"
import { Env } from '@env';
import axios from 'axios';
export const client = axios.create({
  baseURL: Env.API_URL,
});
```

:::note[Important]
Using `import { Env } from '@env';` will import the env from the `src/lib/env.js` file, which export client only env vars.
:::

6. Use `APP_ENV` to load the correct `.env` file :

```bash
APP_ENV=production pnpm start -cc
```

As mentioned earlier, `zod` is used to validate environment variables at runtime and build time. If there are any missing or invalid variables, you'll see an error message with information on what needs to be fixed. Here's an example error message:

```bash
❌ Invalid environment variables: { TEST: [ 'Required' ] }
❌ Missing variables in .env.development file, Make sure all required variables are defined in the .env.development file.
💡 Tip: If you recently updated the .env.development file and the error still persists, try restarting the server with the -c flag to clear the cache.
```

:::note[Important]
As `dotenv` handles variables as strings, you need to convert them to the correct type while reading them from the `process.env` object. check the example below for more details on how to use numbers and boolean values.
:::

## How it works

#### ✅ Validate and parse environment variables

If you take a look at the `env.js` file, you will notice that the file is split into three main parts as shown below:

<Code file="env.js" />

**In the first part** We load the correct `.env` file based on the `APP_ENV` variable using `dotenv` package. If the `APP_ENV` variable is not defined, we default to `development`.

we define some static variables for the app such as the app name, bundle Id and package. While these variables can be added to the `.env` files, we recommend keeping them in the `env.js` file as they are not meant to change. To handle different app variants, you can add suffixes to these variables using the `withEnvSuffix` function.

**In the second part**, we define the `zod` schema for the environment variables.

We split the environment variables into two parts:

- **Client Variables**: Variables that are safe to be exposed to the client and used in the `src` folder.

- **Build Time Variables**: Variables that we don't need on the client-side and are only used in the `app.config.ts`, for example, `SENTRY_AUTH` to upload the source maps to Sentry.

These schemas are used to validate the environment variables. All the environment variables should be added to the correct schema.

We use the `z.infer` utility to infer the environment variables' types from the schema and use it to define the `_clientEnv` and `_buildTimeEnv` objects' type. This means that if you add a new environment variable to the schema, you will get a type error if you don't add it to the correct `_clientEnv` and `_buildTimeEnv` object as well, and vice versa.

**Finally**, in the third part, we merge variables to `_env`, pare it using the zod schema, and return the parsed object as well as the client environment variable, or throw errors in case of invalid or missing variables.

#### ✅ Use and send environment variables to the client

Now it's as easy as importing `Env` , `ClientEnv` and `withEnvSuffix` from the `./env.js` file and use inside our `app.config.ts`, and finally sending client env vars to the client side using `extra` property.

<Code file="app.config.ts" meta="{4,8-10}" />

#### ✅ Type checking for client environment variables

Here, we added a separate file to export all variables that have already been passed in the `extra` property to the client side. We added a little bit of magic to make it type-safe and easy to use.

<Code file="src/lib/env.js" />

Now the environment variables are ready to use in your project. You can access them in your code by importing `Env` from `@env` and using it like this:

```ts title="client.ts"
import { Env } from '@env';
import axios from 'axios';
export const client = axios.create({
  baseURL: Env.API_URL,
});
```
